﻿//////////////////////////////////////////////////////////////////////////////
//
//  Copyright © 1998-2024 Glodon Company Limited.  All rights reserved.
//
//  Use of this software is subject to the terms of the Glodon license
//  agreement provided at the time of installation or download, or which
//  otherwise accompanies this software in either electronic or hard copy form.  
//
//////////////////////////////////////////////////////////////////////////////


#pragma once
#include "GnufBase.h"
#include "GnufBaseUtils.h"
#include "GnufTrash.h"
#include "GnufWithUserData.h"
#include "GnufItemPath.h"
#include "GnufItemDeclaration.h"
#include "GnufInfo.h"

class QMouseEvent;
namespace pugi
{
    class xml_node;
}

namespace gnuf {

    class AppearanceSetting;
    class IItemEventHandler;
    class ItemEventDispatcher;
    class Info;
    class RootItem;
    class Button;
    class QtItem;
    class DragMove;
    class ItemInteriorEvent;
    
    class GNUF_BASE_EXPORT Item
        : public gcmp::WeakReferenceable
        , public gcmp::Trash
        , public WithUserData
    {
        CANNOT_COPY_OR_ASSIGN(Item)
    protected:
        Item(EItemType type) : m_uniqueKey(nullptr), m_path(""), m_type(type) {}
        explicit Item(const char * constructor, EItemType type, Item * parent);
    public:
        virtual ~Item();
        
        // Destroy this opItem. Delete later or immediately.
        static void destroy(gcmp::OwnerPtr< Item > && opItem);

    public:
        /// \return Return the type.
        inline EItemType type() const { return m_type; }
        
        /// \brief Set the key that help searches an item by key.
        /// \note Item tree does not ensure the uniqueness.
        void setKey(const std::wstring &);
        /// \return Return the key.
        const std::wstring & key() const { return m_key(); }

        /// \return Return the class name.
        const gcmp::UniqueString * className() const { return m_className; }

        /// \return Return the parent.
        Item * parent() { return m_wpParent.Get(); }
        /// \return Return the const parent.
        const Item * parent() const { return m_wpParent.Get(); }
        /// \return Return the grandparent.
        Item * grandparent();
        /// \return Return the const grandparent.
        const Item * grandparent() const;
        /// \return Return the great-grandparent.
        Item * greatgrandparent();
        /// \return Return the const great-grandparent.
        const Item * greatgrandparent() const;

        /// \return Return true if the parent's type just is the one.
        bool           parentIsA(const EItemType) const;
        /// \return Return true if the grandparent's type just is the one.
        bool      grandparentIsA(const EItemType) const;
        /// \return Return true if the great-grandparent's type just is the one.
        bool greatgrandparentIsA(const EItemType) const;
        
        /// \return Return true if this item is under the ancestor.
        bool isUnder(const Item * ancestor) const;

        /// \return Return the root item.
        const RootItem * root() const;
        /// \return Return the const root item.
        RootItem * root();

        /// \return Return true if the item is the root item. Otherwise, return false.
        virtual bool isRootItem() const { return false; }

        /// \return Return the path in the item tree.
        const ItemPath & path() const;

        // Cast to the derived class.
        template <class TDerivedItem>
        TDerivedItem * cast() {
            static_assert(std::is_base_of<Item, TDerivedItem>::value,
                "This class must inherit from Item class!");
            if (TDerivedItem::Type == m_type || onIsA(TDerivedItem::Type)) {
                return static_cast<TDerivedItem *>(this);
            }
            return nullptr;
        }
        template <class TDerivedItem>
        const TDerivedItem * cast() const {
            static_assert(std::is_base_of<Item, TDerivedItem>::value,
                "This class must inherit from Item class!");
            if (TDerivedItem::Type == m_type || onIsA(TDerivedItem::Type)) {
                return static_cast<const TDerivedItem *>(this);
            }
            return nullptr;
        }

        /// \return Return false if it's in the child Qt thread.
        bool canAccessQt() const;

        /// \brief Return the Qt item that intends to implement this item.
        virtual QtItem * getQtItem() const { return nullptr; }

        /// \brief Disconnect from the Qt item that intends to implement this item.
        virtual void disconnectFromQtItem();

        /// \return Return the appearance setting.
        virtual const AppearanceSetting * appearanceSetting() const;

        /// \return Return the event dispatcher.
        virtual ItemEventDispatcher * eventDispatcher();

        /// \return Return the scale factor.
        virtual const ScaleFactor & scaleFactor() const;

        /// \brief Set the info.
        void setInfo(const Info *);
        /// \return Return the const info.
        inline const Info * info() const;

        /// \brief Set the visible.
        void setVisible(bool);
        /// \return Return true if visible.
        bool visible() const { return m_visible; }

        /// \brief Set the enabled.
        void setEnabled(bool);
        /// \return Return true if enabled.
        inline bool enabled() const { return m_enabled; }

        typedef std::list< gcmp::WeakPtr< Item > > Children;

        /// \return Return all of the children.
        const Item::Children & children() const { return m_children; }

        /// \brief Transfer a child from me to the new parent.
        bool transferChild(const Item * pChild, Item * pNewParent);

        /// \return Return true if this child exits. Otherwise, return false.
        bool hasChild(const Item * pChild) const;

        virtual void onChildHeightChanged(Item *, int) {}

        /// \brief when visible changed, let it's parent know
        /// \param Item* Item visible chaned
        /// \return 
        virtual void onChildVisibilityChanged(const Item*) {}
        
        /// \brief Set the unique key.
        void setUniqueKey(const gcmp::UniqueString *);
        /// \return Return the unique key.
        const gcmp::UniqueString * uniqueKey() const { return m_uniqueKey; }
        
        /// \brief Set the Qt object name, instead of the one created automatically.
        void setQtObjectName(const std::wstring & qtObjectName) { m_qtObjectName = qtObjectName; }
        /// \return Return the Qt object name.
        const std::wstring & qtObjectName() const { return m_qtObjectName; }

        /// \brief Set the OnEvent.
        void setOnEvent(bool);
        /// \return Return true if this item is on event.
        bool isOnEvent() const { return m_onEvents() > 0; }

        void sendInteriorEvent(ItemInteriorEvent *);

        /// \brief Set the badge visible.
        void setBadgeVisible(bool);
        /// \return Return true if the badge is visible.
        bool isBadgeVisible() const { return m_badgeVisible; }

        virtual void onTableRowSelectionChanged(bool) {}
        
        typedef typename std::function<ETransferingContentMethod(
            const Item *,   // pSender
            const Item *,   // pAcceptor
            const Item *    // pContent
            )> DecideTranferringContentMethod;
        typedef typename std::function<void(
            Item *          // pContentTransferred
            )> PostContentTransferred;
        /// \brief Transfer some contents to the target.
        /// \param pTarget: This is the target to which want to transfer contents.
        ///        This is necessary.
        /// \param onDecideTransferingContentMethod: This callback intends to decide 
        ///        how to transfer a content. This is necessary.
        /// \param onPostContentTransferred: This callback intends to notify after 
        ///        the content transferred. This is optional.
        virtual void transferContentsTo(Item            * pTarget,
            const Item::DecideTranferringContentMethod  & onDecideTransferringContentMethod,
            const Item::PostContentTransferred          & onPostContentTransferred)
        { DBG_WARN(L"Not impelemented!", L"GMEP", L"2022-08-18"); }

        /// \brief Set the provenance.
        void setProvenance(const std::wstring & val) { m_provenance = val; }
        /// \return Return the provenance. By default, it is empty. In some cases,
        /// like transferring, it can be used to remember where this item comes from.
        const std::wstring & provenance() const { return m_provenance; }

        /// \brief item mousepressevent
        bool handlePressed(QMouseEvent*);

        /// \brief dump item content to xml
        void dump(pugi::xml_node& xmlNode);

        virtual bool isAtCell() const;

    protected:
        virtual void onPostInfoChanged() {}
        virtual void onPostVisibilityChanged() {}
        virtual void onPostAbilityChanged() {}
        virtual void onPostAppearanceSettingChanged() {}
        virtual void onDisconnectFromMyQtItem() = 0;
        virtual bool onIsA(const EItemType type) const { return (type == m_type); }
        virtual void onPostBadgeVisibilityChanged() {}
        virtual void onPostScaleFactorChanged() {}
        virtual void onTransferredDueTo(const Item* pSource) {};

        virtual bool onAllowToTransferChild(const Item * pChild, const Item * pNewParent) const {
            // Does not allow, by default. The derived class wants to override it.
            return false;
        }

        void notifyAppearanceSettingChanged();
        void notifyScaleFactorChanged();
        void onPostTransferredDueTo(const Item* pSource);

        virtual void onInteriorEvent(ItemInteriorEvent *);

        // It is very very dangerous to delete an item that is on event.
        // So, please make sure that it is safe, before call this method.
        void setCanBeDeletedIfOnEvent() { m_canBeDeletedIfOnEvent = true; }

        // Inherits from Trash...
        virtual void onDiscarded() override;
        virtual bool onAllowToDelete() const override;

		virtual bool clickOnTable(QMouseEvent*) { return false; };
        
        // 支持子Item扩展Dump自有属性
        virtual void onDumpThisTo(pugi::xml_node&) const {};
    private:
        void eraseUniqueKey();
        void eraseParentChildConnection();
        void notifyChildren(void (Item::*fire)());
        template <class TParameter>
        void notifyChildren(
            void (Item::*fire)(TParameter), TParameter parameter)
        {
            for (auto& child : m_children) {
                if (child.IsValid()) {
                    (child.Get()->*fire)(parameter);
                }
            }
        }

        void addChild(Item *);
        void removeChild(const Item * const);

        const gcmp::UniqueString * m_className;
        const EItemType m_type;
        struct {
            friend void Item::setKey(const std::wstring &);
            const std::wstring & operator()() const { return m_data; }
        private: std::wstring m_data; } m_key;
        gcmp::WeakPtr< Item > m_wpParent;
        Item::Children m_children;
        mutable ItemPath m_path;
        gcmp::WeakPtr< Info > m_wpInfo;
        bool m_visible;
        bool m_enabled;
        const gcmp::UniqueString * m_uniqueKey;
        std::wstring m_qtObjectName;
        struct {
            friend void Item::setOnEvent(bool);
            int operator()() const { return m_data; }
        private: int m_data = 0; } m_onEvents;
        bool m_canBeDeletedIfOnEvent;
        bool m_badgeVisible;
        std::wstring m_provenance;
    };
    
    class GNUF_BASE_EXPORT RootItem : public Item
    {
        CANNOT_COPY_OR_ASSIGN(RootItem)
    protected:
        RootItem(EItemType type) : Item(type) {}
        explicit RootItem(const char * constructor, EItemType type, Item * parent);
        virtual ~RootItem();
    public:
        /// \brief Set the appearance setting.
        void setAppearanceSetting(gcmp::OwnerPtr<AppearanceSetting> &&);

        /// \brief Tweak the given appearance setting.
        void tweakAppearanceSetting(const std::function<void(AppearanceSetting *)> &);

        /// \brief Set the event handler.
        void setEventHandler(IItemEventHandler *);

        /// \brief Set the selected button.
        void setSelectedButton(Button *);

        /// \return Return the selected button.
        Button * selectedButton() const { return m_selectedButton; }

        /// \brief Set the scale factor.
        void setScaleFactor(const ScaleFactor &);

    public:
        // Inherits from Item...
        virtual bool isRootItem() const override final { return true; }
        virtual const AppearanceSetting * appearanceSetting() const override final;
        virtual ItemEventDispatcher * eventDispatcher() override final;
        virtual const ScaleFactor & scaleFactor() const override final;
    private:
        gcmp::OwnerPtr<AppearanceSetting> m_appearanceSetting;
        gcmp::OwnerPtr<ItemEventDispatcher> m_eventDispatcher;
        Button * m_selectedButton;
        ScaleFactor m_scaleFactor;
    };

    template <class TQtItem, class TBaseClass = Item>
    class ItemWith : public TBaseClass
    {
    protected:
        ItemWith(EItemType type) : TBaseClass(type), m_qtItem(nullptr) {}
        explicit ItemWith(const char * constructor, EItemType type, Item * parent)
            : TBaseClass(constructor, type, parent)
            , m_qtItem(nullptr)
        {}
        virtual ~ItemWith()
        {
            // The Qt item should be nullptr, before destruct.
            GBMP_ASSERT(nullptr == m_qtItem);
        }

    public:
        void setQtItem(TQtItem * qtItem)
        {
            if (nullptr == m_qtItem && nullptr != qtItem) {
                m_qtItem = qtItem;
            }
            else if (nullptr != m_qtItem && nullptr == qtItem) {
                m_qtItem = nullptr;
            }
            else {
                GBMP_ASSERT(false);
            }
        }

        // Return the Qt item.
        inline TQtItem * qtItem() const { return (canAccessQt() ? m_qtItem : nullptr); }

        // Return true if the Qt item exits. Otherwise, return false.
        inline bool hasQtItem() const { return (nullptr != m_qtItem); }

    private:
        TQtItem * m_qtItem;
    };
    
    struct ItemDestroyer
    {
        static void destroy(gcmp::OwnerPtr<Item> &&);

        template <typename TItemSet>
        static void destroySet(TItemSet &);

        template <typename TItemMap>
        static void destroyMap(TItemMap &);
    };
}

template <typename TItemSet>
void gnuf::ItemDestroyer::destroySet(TItemSet & opItemSet)
{
    if (opItemSet.empty())
        return;

    for (auto itor = opItemSet.begin(); itor != opItemSet.end(); ++itor)
    {
        gnuf::ItemDestroyer::destroy( TransferOwnership( *itor ) );
    }
    opItemSet.clear();
}

template <typename TItemMap>
void gnuf::ItemDestroyer::destroyMap(TItemMap & opItemMap)
{
    if (opItemMap.empty())
        return;

    for (auto itor = opItemMap.begin(); itor != opItemMap.end(); ++itor)
    {
        gnuf::ItemDestroyer::destroy( TransferOwnership( (*itor).second ) );
    }
    opItemMap.clear();
}
